
#include "utils.h"
#include "varint.h"

#include <attest/attest.h>

#include <pup/time.h>

ATTEST(varint, encode_bool)
{
  uint8_t buffer[4];
  uint8_t* b;
  b = cfers_encode_bool(buffer, true);
  ATTEST_INT_EQ(buffer+1, b);
  ATTEST_INT_EQ(1, buffer[0]);
  b = cfers_encode_bool(buffer, false);
  ATTEST_INT_EQ(buffer+1, b);
  ATTEST_INT_EQ(0, buffer[0]);
}

ATTEST(varint, encode_int32_zero)
{
  uint8_t buffer[32];
  uint8_t* b = cfers_encode_int32(buffer, 0);
  ATTEST_EQ(buffer+1, b);
  ATTEST_EQ(0, buffer[0]);
}

ATTEST(varint, encode_int32_one_byte)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = 0; i != 128; ++i) {
    b = cfers_encode_int32(buffer, i);
    ATTEST_EQ(buffer+1, b);
    ATTEST_EQ(COLFERS_STATIC_CAST(uint8_t,i), buffer[0]);
  }
}

ATTEST(varint, encode_int32_two_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = 128; i != COLFERS_POWER_OF_TWO32(14); ++i) {
    b = cfers_encode_int32(buffer, i);
    ATTEST_EQ(buffer+2, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_FALSE(buffer[1] & 0x80);
    int32_t value = (COLFERS_STATIC_CAST(int32_t,buffer[1]) << 7) | (buffer[0] & 0x7F);
    ATTEST_EQ(i, value);
  }
}

ATTEST(varint, encode_int32_three_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = COLFERS_POWER_OF_TWO32(14); i != COLFERS_POWER_OF_TWO32(21); ++i) {
    b = cfers_encode_int32(buffer, i);
    ATTEST_EQ(buffer+3, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_FALSE(buffer[2] & 0x80);
    int32_t value = 
      (COLFERS_STATIC_CAST(int32_t,buffer[2]) << 14) | 
      ((COLFERS_STATIC_CAST(int32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i, value);
  }
}

ATTEST(varint, encode_int32_four_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = COLFERS_POWER_OF_TWO32(21); i != COLFERS_POWER_OF_TWO32(28); ++i) {
    b = cfers_encode_int32(buffer, i);
    ATTEST_EQ(buffer+4, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_TRUE(buffer[2] & 0x80);
    ATTEST_FALSE(buffer[3] & 0x80);
    int32_t value = 
      (COLFERS_STATIC_CAST(int32_t,buffer[3]) << 21) | 
      ((COLFERS_STATIC_CAST(int32_t,buffer[2]) & 0x7F) << 14) |
      ((COLFERS_STATIC_CAST(int32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i, value);
  }
}

ATTEST(varint, encode_int32_five_bytes_positive)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint64_t i = COLFERS_POWER_OF_TWO64(28); i != COLFERS_POWER_OF_TWO32(31); ++i) {
    int32_t i32 = COLFERS_STATIC_CAST(int32_t,i);
    b = cfers_encode_int32(buffer, i32);
    ATTEST_EQ(buffer+5, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_TRUE(buffer[2] & 0x80);
    ATTEST_TRUE(buffer[3] & 0x80);
    ATTEST_FALSE(buffer[4] & 0x80);
    int32_t value = 
      (COLFERS_STATIC_CAST(int32_t,buffer[4]) << 28) | 
      ((COLFERS_STATIC_CAST(int32_t,buffer[3]) & 0x7F) << 21) |
      ((COLFERS_STATIC_CAST(int32_t,buffer[2]) & 0x7F) << 14) |
      ((COLFERS_STATIC_CAST(int32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i32, value);
  }
}

ATTEST(varint, encode_int32_negative)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = -1; i != -2000000000; --i) {
    b = cfers_encode_int32(buffer, i);
    ATTEST_EQ(buffer+5, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_TRUE(buffer[2] & 0x80);
    ATTEST_TRUE(buffer[3] & 0x80);
    ATTEST_FALSE(buffer[4] & 0x80);
    int32_t value = 
      (COLFERS_STATIC_CAST(int32_t,buffer[4]) << 28) | 
      ((COLFERS_STATIC_CAST(int32_t,buffer[3]) & 0x7F) << 21) |
      ((COLFERS_STATIC_CAST(int32_t,buffer[2]) & 0x7F) << 14) |
      ((COLFERS_STATIC_CAST(int32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i, value);
  }
}

ATTEST(varint, encode_int32_perf)
{
  uint8_t buffer[256];
  uint8_t* b;
  pup_timestamp_t before = pup_timestamp_now();
  for(int32_t i = 0; i != 10000000; ++i)
    b = cfers_encode_uint32(buffer, i);
  pup_timestamp_t after = pup_timestamp_now();
  pup_timespan_t timespan = pup_timespan_diff(before, after);
  uint64_t nanos = pup_timespan_nanos(&timespan);
  printf("encode int32: %f ns\n", nanos / 10000000.0);
  if (b == NULL)
    printf("impossible!\n");
}

ATTEST(varint, encode_sint32_one_byte_positive)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = 0; i != 64; ++i) {
    b = cfers_encode_sint32(buffer, i);
    ATTEST_EQ(buffer+1, b);
    ATTEST_INT_EQ(2*COLFERS_STATIC_CAST(uint8_t,i), buffer[0]);
  }
}

ATTEST(varint, encode_sint32_one_byte_negative)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(int32_t i = -1; i != -64; --i) {
    b = cfers_encode_sint32(buffer, i);
    ATTEST_EQ(buffer+1, b);
    int32_t value = buffer[0];
    ATTEST_INT_EQ(i, -value/2-1);
  }
}

ATTEST(varint, encode_uint32_zero)
{
  uint8_t buffer[32];
  uint8_t* b = cfers_encode_uint32(buffer, 0);
  ATTEST_EQ(buffer+1, b);
  ATTEST_EQ(0, buffer[0]);
}

ATTEST(varint, encode_uint32_one_byte)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint32_t i = 0; i != 128; ++i) {
    b = cfers_encode_uint32(buffer, i);
    ATTEST_EQ(buffer+1, b);
    ATTEST_EQ(COLFERS_STATIC_CAST(uint8_t,i), buffer[0]);
  }
}

ATTEST(varint, encode_uint32_two_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint32_t i = 128; i != COLFERS_POWER_OF_TWO32(14); ++i) {
    b = cfers_encode_uint32(buffer, i);
    ATTEST_EQ(buffer+2, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_FALSE(buffer[1] & 0x80);
    uint32_t value = (COLFERS_STATIC_CAST(uint32_t,buffer[1]) << 7) | (buffer[0] & 0x7F);
    ATTEST_EQ(i, value);
  }
}

ATTEST(varint, encode_uint32_three_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint32_t i = COLFERS_POWER_OF_TWO32(14); i != COLFERS_POWER_OF_TWO32(21); ++i) {
    b = cfers_encode_uint32(buffer, i);
    ATTEST_EQ(buffer+3, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_FALSE(buffer[2] & 0x80);
    uint32_t value = 
      (COLFERS_STATIC_CAST(uint32_t,buffer[2]) << 14) | 
      ((COLFERS_STATIC_CAST(uint32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i, value);
  }
}

ATTEST(varint, encode_uint32_four_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint32_t i = COLFERS_POWER_OF_TWO32(21); i != COLFERS_POWER_OF_TWO32(28); ++i) {
    b = cfers_encode_uint32(buffer, i);
    ATTEST_EQ(buffer+4, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_TRUE(buffer[2] & 0x80);
    ATTEST_FALSE(buffer[3] & 0x80);
    uint32_t value = 
      (COLFERS_STATIC_CAST(uint32_t,buffer[3]) << 21) | 
      ((COLFERS_STATIC_CAST(uint32_t,buffer[2]) & 0x7F) << 14) |
      ((COLFERS_STATIC_CAST(uint32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i, value);
  }
}

ATTEST(varint, encode_uint32_five_bytes)
{
  uint8_t buffer[32];
  uint8_t* b;
  for(uint64_t i = COLFERS_POWER_OF_TWO64(28); i != COLFERS_POWER_OF_TWO64(32); ++i) {
    uint32_t i32 = COLFERS_STATIC_CAST(int32_t,i);
    b = cfers_encode_uint32(buffer, i32);
    ATTEST_EQ(buffer+5, b);
    ATTEST_TRUE(buffer[0] & 0x80);
    ATTEST_TRUE(buffer[1] & 0x80);
    ATTEST_TRUE(buffer[2] & 0x80);
    ATTEST_TRUE(buffer[3] & 0x80);
    ATTEST_FALSE(buffer[4] & 0x80);
    uint32_t value = 
      (COLFERS_STATIC_CAST(uint32_t,buffer[4]) << 28) | 
      ((COLFERS_STATIC_CAST(uint32_t,buffer[3]) & 0x7F) << 21) |
      ((COLFERS_STATIC_CAST(uint32_t,buffer[2]) & 0x7F) << 14) |
      ((COLFERS_STATIC_CAST(uint32_t,buffer[1]) & 0x7F) << 7) |
      (buffer[0] & 0x7F);
    ATTEST_INT_EQ(i32, value);
  }
}

ATTEST(varint, encode_uint32_perf)
{
  uint8_t buffer[256];
  uint64_t x = 0;
  pup_timestamp_t before = pup_timestamp_now();
  for(int32_t i = 0; i != 10000000; ++i)
    x += (uint64_t)cfers_encode_uint32(buffer, i);
  pup_timestamp_t after = pup_timestamp_now();
  pup_timespan_t timespan = pup_timespan_diff(before, after);
  uint64_t nanos = pup_timespan_nanos(&timespan);
  printf("encode uint32: %f ns\n", nanos / 10000000.0);
  if (x == 1)
    printf("impossible!\n");
}
